package visitor;

import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Vector;

import syntaxtree.*;
import symbol.ClassTable;
import symbol.MethodTable;
import symbol.Symbol;


public class SymbolTableCreator implements Visitor{

	private Vector<ClassTable> classes;
	private Dictionary<Symbol,Integer> map_class;

	//State
	private int current;
	private MethodTable metodoCriando;

	public SymbolTableCreator(){
		classes   = new Vector<ClassTable>();
		map_class = new Hashtable<Symbol,Integer>();	
	}

	public Vector<ClassTable> getClasses() {
		return classes;
	}

	public Dictionary<Symbol, Integer> getMap_class() {
		return map_class;
	}



	// MainClass m;
	// ClassDeclList cl;
	public void visit(Program n) {

		ClassTable nova = new ClassTable(Symbol.symbol(n.m.i1.s),null);
		current = 0;
		map_class.put(nova.className(), current );
		classes.add(nova);

		n.m.accept(this);

		for ( int i = 0; i < n.cl.size(); i++ ) {
			current++;
			n.cl.elementAt(i).accept(this);    
		}
	}

	// Identifier i1,i2;
	// Statement s;
	public void visit(MainClass n) {
		ClassTable c = classes.elementAt(current);

		metodoCriando = new MethodTable(Symbol.symbol("main"), Symbol.symbol("public static void"));
		
		try {
			metodoCriando.putParams(Symbol.symbol(n.i2.s), Symbol.symbol("String []"));
		} catch (Exception e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		n.s.accept(this);

		try {
			c.putMethod(metodoCriando);
		} catch (Exception e) {
			e.printStackTrace();
		}

		//n.i1.accept(this);    
		//n.i2.accept(this);     

	}

	// Identifier i;
	// VarDeclList vl;
	// MethodDeclList ml;
	public void visit(ClassDeclSimple n) {

		ClassTable nova = new ClassTable(Symbol.symbol(n.i.s),null);
		map_class.put(nova.className(), current );
		classes.add(nova);
		metodoCriando = null;

		//n.i.accept(this);

		for ( int i = 0; i < n.vl.size(); i++ ) {
			n.vl.elementAt(i).accept(this);
		}

		for ( int i = 0; i < n.ml.size(); i++ ) {
			n.ml.elementAt(i).accept(this);
		}

	}

	// Identifier i;
	// Identifier j;
	// VarDeclList vl;
	// MethodDeclList ml;
	public void visit(ClassDeclExtends n) {

		ClassTable nova = new ClassTable(Symbol.symbol(n.i.s),Symbol.symbol(n.j.s));
		map_class.put(nova.className(), current );
		classes.add(nova);
		metodoCriando = null;

		for ( int i = 0; i < n.vl.size(); i++ ) {
			n.vl.elementAt(i).accept(this);
		}
		for ( int i = 0; i < n.ml.size(); i++ ) {
			n.ml.elementAt(i).accept(this);
		}
	}  

	// Type t;
	// Identifier i;
	public void visit(VarDecl n) {
		//n.t.accept(this);
		//n.i.accept(this);

		if( metodoCriando == null ){

			ClassTable classeAtual = classes.elementAt(current);

			try {
				classeAtual.putFields(Symbol.symbol(n.i.s), Symbol.symbol(n.t.whichType()));
			} catch (Exception e) {
				e.printStackTrace();
			}

		} else try {
			metodoCriando.putLocals(Symbol.symbol(n.i.s), Symbol.symbol(n.t.whichType()));
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	// Type t;
	// Identifier i;
	// FormalList fl;
	// VarDeclList vl;
	// StatementList sl;
	// Exp e;
	public void visit(MethodDecl n) {

		ClassTable atual = classes.elementAt(current);
		metodoCriando = new MethodTable(Symbol.symbol(n.i.s),Symbol.symbol(n.t.whichType()));

		try {
			atual.putMethod(metodoCriando);
		} catch (Exception e) {
			e.printStackTrace();
		}

		//n.t.accept(this);
		//n.i.accept(this);

		// Parâmetros
		for ( int i = 0; i < n.fl.size(); i++ ) {
			n.fl.elementAt(i).accept(this);
		}

		// Declaração de Variáveis locais
		for ( int i = 0; i < n.vl.size(); i++ ) {
			n.vl.elementAt(i).accept(this);
		}

		// Statements
		for ( int i = 0; i < n.sl.size(); i++ ) {
			n.sl.elementAt(i).accept(this);
		}

		//n.e.accept(this);
	}

	// Type t;
	// Identifier i;
	// Parâmetro
	public void visit(Formal n) {
		//n.t.accept(this);
		//n.i.accept(this);
		
		try {
			metodoCriando.putParams(Symbol.symbol(n.i.s),Symbol.symbol(n.t.whichType()));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void visit(IntArrayType n) {
	}

	public void visit(BooleanType n) {
	}

	public void visit(IntegerType n) {
	}

	public void visit(IdentifierType n) {
	}

	// StatementList sl;
	public void visit(Block n) {
		for ( int i = 0; i < n.sl.size(); i++ ) {
			n.sl.elementAt(i).accept(this);
		}
	}

	// Exp e;
	// Statement s1,s2;
	public void visit(If n) {

		// Expressão booleana
		//n.e.accept(this);

		// Se for verdade
		n.s1.accept(this);

		// Se for falso
		n.s2.accept(this);
	}

	// Exp e;
	// Statement s;
	public void visit(While n) {

		// Expressão Booleana
		//n.e.accept(this);

		// Conjunto de statements
		n.s.accept(this);
	}

	// Exp e;
	public void visit(Print n) {
		//n.e.accept(this);
	}

	// Identifier i;
	// Exp e;
	public void visit(Assign n) {
		//n.i.accept(this);
		//n.e.accept(this);
	}

	// Identifier i;
	// Exp e1,e2;
	public void visit(ArrayAssign n) {
		//n.i.accept(this);
		//n.e1.accept(this);
		//n.e2.accept(this);
	}

	// Exp e1,e2;
	public void visit(And n) {
		//n.e1.accept(this);
		// n.e2.accept(this);

	}

	// Exp e1,e2;
	public void visit(LessThan n) {
		//n.e1.accept(this);
		//n.e2.accept(this);
	}

	// Exp e1,e2;
	public void visit(Plus n) {
		//n.e1.accept(this);
		//n.e2.accept(this);
	}

	// Exp e1,e2;
	public void visit(Minus n) {
		//n.e1.accept(this);
		//n.e2.accept(this);
	}

	// Exp e1,e2;
	public void visit(Times n) {
		//n.e1.accept(this);
		//n.e2.accept(this);
	}

	// Exp e1,e2;
	public void visit(ArrayLookup n) {
		//n.e1.accept(this);
		//n.e2.accept(this);
	}

	// Exp e;
	public void visit(ArrayLength n) {
		//n.e.accept(this);
	}

	// Exp e;
	// Identifier i;
	// ExpList el;
	public void visit(Call n) {
		/*n.e.accept(this);
    n.i.accept(this);
    for ( int i = 0; i < n.el.size(); i++ ) {
        n.el.elementAt(i).accept(this);
    }*/
	}

	// int i;
	public void visit(IntegerLiteral n) {
	}

	public void visit(True n) {
	}

	public void visit(False n) {
	}

	// String s;
	public void visit(IdentifierExp n) {
	}

	public void visit(This n) {
	}

	// Exp e;
	public void visit(NewArray n) {
		//n.e.accept(this);
	}

	// Identifier i;
	public void visit(NewObject n) {
	}

	// Exp e;
	public void visit(Not n) {
		//n.e.accept(this);
	}

	// String s;
	public void visit(Identifier n) {
	}

	public void imprime(){	  
		for (int i = 0; i < this.classes.size(); i++) {
			System.out.println("Classe");
			System.out.println(" -- " + this.classes.get(i).className().toString());		  

			System.out.println("Metodo");
			this.classes.get(i).imprimeClasse();	  

		}	  
	}

	public void setExtends(){
		for (ClassTable classe : classes) {
			
			if(classe.extendClass()!=null)
			{
				ClassTable Extends;
				System.out.println(classe.className().toString());
				System.out.println(classe.extendClass().toString());
				Extends = classes.get(map_class.get(classe.extendClass()));
				if(classe.extendClass() ==	Extends.className())
					classe.setExtendsTable(Extends);
			}
		}	  

	}

	@Override
	public void visit(GreaterThan n) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void visit(GreaterEqualThan n) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void visit(LessEqualThan n) {
		// TODO Auto-generated method stub
		
	}
	
}
